package com.jms.web.mvc.servlet;

import com.jms.framework.context.ApplicationContext;
import com.jms.framework.exception.PropertyAlreadySetException;
import com.jms.web.mvc.context.WebMvcContext;
import com.jms.web.mvc.handler.MethodHandler;
import com.jms.web.mvc.interceptor.GeneralInterceptorChain;
import com.jms.web.mvc.resource.StaticResourceManagement;
import com.jms.web.mvc.thread.MvcThreadLocalContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * DispatcherServlet类是MVC框架中的核心控制器，负责处理所有的HTTP请求
 * 它通过反射机制调用对应的处理器方法，并处理静态资源请求
 * @author jms
 * @since 2025-01-29
 * @version 1.0.0
 */
public class DispatcherServlet extends HttpServlet {

    private volatile static DispatcherServlet instance;

    private WebMvcContext webMvcContext;

    // 私有构造方法，确保单例
    private DispatcherServlet() {
    }

    /**
     * 获取DispatcherServlet的实例
     * @return DispatcherServlet的实例
     */
    public static DispatcherServlet getInstance() {
        if (instance == null) {
            synchronized (DispatcherServlet.class) {
                if (instance == null) {
                    instance = new DispatcherServlet();
                }
            }
        }
        return instance;
    }

    /**
     * 设置WebMvcContext上下文对象
     * @param webMvcContext WebMvcContext对象，用于提供MVC相关配置和映射信息
     * @throws PropertyAlreadySetException 如果webMvcContext已经被设置过，则抛出此异常
     */
    public void setWebMvcContext(WebMvcContext webMvcContext) {
        if (this.webMvcContext != null) {
            throw new PropertyAlreadySetException(ApplicationContext.class, "applicationContext");
        }
        this.webMvcContext = webMvcContext;
    }

    /**
     * 初始化方法，加载静态资源路径和初始化URL映射
     * @throws ServletException 如果初始化失败，则抛出此异常
     */
    @Override
    public void init() throws ServletException {
        super.init();
    }

    /**
     * 服务方法，处理HTTP请求
     * @param req HTTP请求对象，封装了客户端的请求信息
     * @param resp HTTP响应对象，用于向客户端返回响应数据
     * @throws ServletException 如果服务方法执行过程中发生错误，则抛出此异常
     * @throws IOException 如果I/O操作失败，则抛出此异常
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MvcThreadLocalContext.setRequest(req);
        MvcThreadLocalContext.setResponse(resp);

        doService(req, resp);

        MvcThreadLocalContext.remove();
    }

    private void doService(HttpServletRequest req, HttpServletResponse resp) {
        String requestMethod = req.getMethod();
        String requestUrl = req.getRequestURI();

        // 处理静态资源请求
        if (webMvcContext.getStaticResourcePath() != null) {
            for (String path : webMvcContext.getStaticResourcePath()) {
                if (requestUrl.startsWith(path)) {
                    StaticResourceManagement.getStaticResource(resp, requestUrl);
                    return;
                }
            }
        }

        MethodHandler methodHandler = null;
        for (MethodHandler methodHandler1 : webMvcContext.getMethodHandlers()) {
            if (methodHandler1.matcher(requestUrl)) {
                methodHandler = methodHandler1;
                if (!requestMethod.equals(methodHandler.getRequestType())) {
                    continue;
                }
                break;
            }
        }

        // 处理动态资源请求
        if (methodHandler == null) {
            resp.setStatus(404);
            return;
        }
        if (!requestMethod.equals(methodHandler.getRequestType())) {
            resp.setStatus(405);
            return;
        }

        GeneralInterceptorChain interceptorChain = new GeneralInterceptorChain(webMvcContext, req, resp, methodHandler);
        interceptorChain.doInterceptor(req, resp, methodHandler);
    }
}
